
#include "timer.h"

#include <thread>


#if defined(__GNUC__) || defined(__MINGW__) || defined(__MINGW32__)
    #pragma GCC diagnostic push
    #pragma GCC diagnostic ignored "-Wconversion"
#endif

namespace Den
{
    namespace{

        typedef Timer::EventWakeUp  
            EventWakeUp;
        typedef Timer::MicroSeconds 
            MicroSeconds;
        typedef Timer::Seconds      
            Seconds;
        typedef Timer::Clock        
            Clock;
        
        typedef std::chrono::microseconds 
            MSec;
//         typedef std::chrono::seconds      
//             Sec;

        void Sleeping(const MicroSeconds pause, const EventWakeUp& wakeup)
        {
            do
            {
                std::this_thread::sleep_for(std::chrono::microseconds(pause) );

            }while ( wakeup && wakeup() );
        }

    }//namespace

    MicroSeconds Timer::QuantMS()
    {
        static Clock::time_point last;
        const Clock::time_point now = Clock::now();
        const auto result = std::chrono::duration_cast<MSec>(now - last);
        last = now;
        return static_cast<MicroSeconds>( result.count() );
    }
    Seconds Timer::QuantS() 
    {
        return static_cast<Seconds>
            (QuantMS()/1000000.0);
    }

    Timer::Timer(const int id)
        :mId(id)
        ,mDelay()
        ,mEvent()
        ,mMark()
    {}

    Timer::Timer( const Seconds delay, const EventWakeUp& wakeup, const int id)
        :mId(id)
        ,mDelay()
        ,mEvent()
        ,mMark()
    {
        Restart(delay,wakeup);
    }

    Timer::Timer( const MicroSeconds delay, const EventWakeUp& wakeup, const int id)
        :mId(id)
        ,mDelay()
        ,mEvent()
        ,mMark()
    {
        Restart(delay,wakeup);
    }
    
    
    void Timer::Restart()
    {
        Mark();
    }

    void Timer::Restart(const Seconds delay, const EventWakeUp& wakeup)
    {
        mDelay = static_cast<MicroSeconds>(delay*1000000);
        mEvent = wakeup;
        Mark();
    }
    void Timer::Restart(const MicroSeconds delay,const EventWakeUp& wakeup)
    {
        mDelay = delay;
        mEvent = wakeup;
        Mark();
    }

    Timer::~Timer(){}

    void Timer::Mark() { mMark = Clock::now(); }

    MicroSeconds Timer::ElapsedMS()const
    {
        return static_cast<MicroSeconds>
            (std::chrono::duration_cast<MSec>(Clock::now() - mMark).count());
    }
    Seconds Timer::ElapsedS()const
    {
        return static_cast<Seconds>
            ( static_cast<double>(ElapsedMS())/1000000.0 );
    }

    MicroSeconds Timer::RemainingMS()const
    {
        if(mMark == Clock::time_point() || mDelay==0) 
            return 0;
        
        const auto elapsed = ElapsedMS(); 
        return (mDelay<=elapsed)? 0: mDelay-elapsed;
    }
    Seconds Timer::RemainingS()const
    {
        if(mMark == Clock::time_point() || mDelay==0) 
            return 0;

        const auto elapsed = ElapsedMS(); 
        return (mDelay<=elapsed)? 0.0: 
            static_cast<double>(mDelay-elapsed)/1000000.0;
    }

    bool Timer::TickTock()
    {
        if(mMark == Clock::time_point())
            return false;

        if(RemainingMS()!=0)
            return true;

        if(mEvent && mEvent())
            Mark();
        else
            mMark=Clock::time_point();
        
        return false;
    }

//-----------------------------------------------------------
    void Timer::BackGround(const Timer::Seconds delay, const Timer::EventWakeUp& wakeup)
    {
        const MicroSeconds pause = static_cast<MicroSeconds>(delay*1000000);
        auto lambda = [pause, wakeup ](){ Sleeping(pause, wakeup); };
        std::thread(lambda).detach();
    }

    void Timer::Sleep(const Timer::Seconds delay, const Timer::EventWakeUp& wakeup)
    {
        const auto pause = static_cast<MicroSeconds>(delay*1000000);
        Sleeping(pause, wakeup);
    }
    
}//namespace Den


#if 0
class Timer {
    typedef std::chrono::high_resolution_clock 
        high_resolution_clock;
    typedef std::chrono::milliseconds 
        milliseconds;
public:
    explicit Timer(bool run = false)
    {
        if (run)
            Reset();
    }
    void Reset()
    {
        _start = high_resolution_clock::now();
    }
    milliseconds Elapsed() const
    {
        return std::chrono::duration_cast<milliseconds>(high_resolution_clock::now() - _start);
    }
    template <typename T, typename Traits>
    friend std::basic_ostream<T, Traits>& operator<<(std::basic_ostream<T, Traits>& out, const Timer& timer)
    {
        return out << timer.Elapsed().count();
    }
private:
    high_resolution_clock::time_point _start;
};
#endif

#if defined(__GNUC__) || defined(__MINGW__) || defined(__MINGW32__)
    #pragma GCC diagnostic pop
#endif
